import akshare as ak
import pandas as pd
import json
import os
from datetime import datetime
import time
import random
from dateutil.relativedelta import relativedelta
import tushare as ts

import ts.common.base_profile as pf

ts.set_token(pf.token)
pro = ts.pro_api()

def daily_to_weekly(df):
    
    """
    将日线数据转换为周线数据
    
    参数:
    df (pandas.DataFrame): 日线数据，包含日期、开盘、最高、最低、收盘、成交量、成交额等列
    
    返回:
    pandas.DataFrame: 周线数据
    """
    if df is None or df.empty:
        return df
    
    # 确保日期列为datetime类型
    if '日期' in df.columns:
        df['日期'] = pd.to_datetime(df['日期'])
    
    # 按周分组
    df['年周'] = df['日期'].dt.year.astype(str) + '-' + df['日期'].dt.isocalendar().week.astype(str).str.zfill(2)
    
    # 聚合周线数据
    weekly_data = []
    for week, group in df.groupby('年周'):
        weekly_row = {
            '日期': group['日期'].max(),  # 该周最后一个交易日
            '开盘': group['开盘'].iloc[0],  # 该周第一个交易日开盘
            '收盘': group['收盘'].iloc[-1],  # 该周最后一个交易日收盘
            '最高': group['最高'].max(),  # 该周内最高价
            '最低': group['最低'].min(),  # 该周内最低价
        }
        
        # 累加成交量和成交额
        if '成交量' in group.columns:
            weekly_row['成交量'] = group['成交量'].sum()
        if '成交额' in group.columns:
            weekly_row['成交额'] = group['成交额'].sum()
        
        weekly_data.append(weekly_row)
    
    # 创建周线DataFrame
    weekly_df = pd.DataFrame(weekly_data)
    
    # 按日期排序
    if not weekly_df.empty:
        weekly_df = weekly_df.sort_values('日期')
        
        # 计算周涨跌幅
        # 周涨跌幅 = (当前周收盘价 - 上周收盘价) / 上周收盘价 * 100
        if '收盘' in weekly_df.columns:
            weekly_df['涨跌幅'] = weekly_df['收盘'].pct_change() * 100
            weekly_df['涨跌幅'] = weekly_df['涨跌幅'].round(2)  # 保留2位小数
    
    return weekly_df

def get_stock_weekly_data(config,start_date:str,end_date:str):
    name = config['name']
    stock_code = config['tsCode']
    stock_type = config['itemType']
    exchange = config['exchange']
    """
    获取指定股票或ETF的周线数据

    参数:
    stock_code (str): 股票或ETF代码（不包含交易所后缀）
    stock_type (str): 类型，'STOCK'表示股票，'FUND'表示ETF

    返回:
    pandas.DataFrame: 周线数据
    """


    try:
        if stock_type == 'STOCK':
            # 处理股票代码，去除交易所后缀
            code = stock_code.split('.')[0]
            if exchange == 'HK':
                time.sleep(1 + random.random() * 1)
                stock_weekly = ak.stock_hk_hist(symbol=code,
                                                adjust='qfq',
                                                period="weekly",
                                                start_date=start_date,
                                                end_date=end_date)
            else:
                # 使用Tushare的weekly接口获取A股周线数据（非HK市场）
                df = pro.weekly(
                    ts_code=stock_code,
                    trade_date="",
                    start_date=start_date,
                    end_date=end_date,
                    limit="",
                    offset="",
                    fields="ts_code,trade_date,close,open,high,low,pre_close,change,pct_chg,vol,amount"
                )
                # 字段映射从tushare->标准输出
                field_mapping = {
                    'trade_date': '日期',
                    'open': '开盘',
                    'high': '最高',
                    'low': '最低',
                    'close': '收盘',
                    'amount': '成交额',
                    'pct_chg':"涨跌幅",
                    'vol': '成交量'
                }
                # 重命名
                df = df.rename(columns=field_mapping)
                # 挑选需要的列
                required_cols = ['日期', '开盘', '最高', '最低', '收盘', '成交额', '成交量',"涨跌幅"]
                available_cols = [col for col in required_cols if col in df.columns]
                df = df[available_cols]
                # 日期列转为datetime
                if '日期' in df.columns:
                    df['日期'] = pd.to_datetime(df['日期'])
                # 确保涨跌幅字段格式统一：百分比形式，保留2位小数
                if '涨跌幅' in df.columns:
                    df['涨跌幅'] = df['涨跌幅'] * 100
                    df['涨跌幅'] = df['涨跌幅'].round(2)
                return df
        elif stock_type == 'FUND':
           
            # 处理基金代码，去除交易所后缀
            code = stock_code.split('.')[0]
            
            # 如果是香港基金，使用原有逻辑
            if exchange == 'HK':
                time.sleep(1 + random.random() * 1)
                # 直接使用fund_etf_hist_em接口获取前复权周线数据
                print(f"尝试使用fund_etf_hist_em接口获取{code}周线数据...")
                try:
                    etf_weekly = ak.fund_etf_hist_em(
                        symbol=code,
                        period="weekly",
                        adjust="qfq",
                        start_date=start_date,
                        end_date=end_date
                    )

                    if etf_weekly is not None and not etf_weekly.empty:
                        # 确保数据包含必要的列
                        required_cols = ['日期', '开盘', '最高', '最低', '收盘', '成交量',"涨跌幅"]
                        missing_cols = [col for col in required_cols if col not in etf_weekly.columns]

                        if missing_cols:
                            print(f"警告: {code}数据缺少必要的列: {missing_cols}")
                            # 尝试映射常见的列名
                            col_mapping = {
                                '交易日期': '日期',
                                '开盘价': '开盘',
                                '最高价': '最高',
                                '最低价': '最低',
                                '收盘价': '收盘',
                                '涨跌幅': "涨跌幅",
                                '成交金额': '成交额',
                                '成交数量': '成交量'
                            }

                            for old_col, new_col in col_mapping.items():
                                if old_col in etf_weekly.columns and new_col not in etf_weekly.columns:
                                    etf_weekly = etf_weekly.rename(columns={old_col: new_col})

                            # 再次检查是否还有缺失的列
                            missing_cols = [col for col in required_cols if col not in etf_weekly.columns]
                            if missing_cols:
                                print(f"无法映射{code}的必要列: {missing_cols}")
                                return None
                        
                        # 确保日期列为datetime类型
                        etf_weekly['日期'] = pd.to_datetime(etf_weekly['日期'])
                        
                        # 确保涨跌幅字段格式统一：百分比形式，保留2位小数
                        if '涨跌幅' in etf_weekly.columns:
                            etf_weekly['涨跌幅'] = etf_weekly['涨跌幅'].round(2)
                        
                        return etf_weekly
                    else:
                        print(f"fund_etf_hist_em接口返回空数据")
                        etf_weekly = None
                except Exception as e:
                    print(f"fund_etf_hist_em接口获取{code}失败: {e}")
                    etf_weekly = None
            # 如果是内地基金（SSE或SZSE），使用tushare接口
            elif exchange in ['SSE', 'SZSE']:
                print(f"尝试使用ts.fund_daily接口获取{stock_code}日线数据...")
                try:
                    # 调用 fund_daily 接口获取日线数据
                    fund_daily = pro.fund_daily(
                        ts_code=stock_code,
                        start_date=start_date,
                        end_date=end_date
                    )
                    
                    if fund_daily is not None and not fund_daily.empty:
                        print(f"成功获取{len(fund_daily)}条日线数据")
                        
                        # 获取复权因子
                        print(f"尝试使用ts.fund_adj接口获取{stock_code}复权因子...")
                        fund_adj = pro.fund_adj(
                            ts_code=stock_code,
                            start_date=start_date,
                            end_date=end_date
                        )
                        
                        # 合并复权因子到日线数据
                        if fund_adj is not None and not fund_adj.empty:
                            print(f"成功获取{len(fund_adj)}条复权因子数据")
                            # 确保按日期排序
                            fund_adj = fund_adj.sort_values('trade_date')
                            # 计算前复权价格：前复权 = 当日价格 ÷ 最新复权因子
                            # 获取最新复权因子（最新日期的复权因子）
                            latest_adj_factor = fund_adj['adj_factor'].iloc[-1] if len(fund_adj) > 0 else 1.0
                            print(f"使用最新复权因子: {latest_adj_factor}")
                            
                            # 计算前复权价格
                            fund_daily['open_qfq'] = fund_daily['open'] / latest_adj_factor
                            fund_daily['high_qfq'] = fund_daily['high'] / latest_adj_factor
                            fund_daily['low_qfq'] = fund_daily['low'] / latest_adj_factor
                            fund_daily['close_qfq'] = fund_daily['close'] / latest_adj_factor
                            
                            # 映射字段并转换日线为周线
                            field_mapping = {
                                'trade_date': '日期',
                                'open_qfq': '开盘',
                                'high_qfq': '最高',
                                'low_qfq': '最低',
                                'close_qfq': '收盘',
                                'pct_chg': "涨跌幅",
                                'amount': '成交额',
                                'vol': '成交量'
                            }
                            
                            # 重命名列
                            fund_daily = fund_daily.rename(columns=field_mapping)
                            
                            # 确保只保留需要的列
                            required_cols = ['日期', '开盘', '最高', '最低', '收盘', '成交量', '成交额',"涨跌幅"]
                            available_cols = [col for col in required_cols if col in fund_daily.columns]
                            fund_daily = fund_daily[available_cols]
                            
                            # 确保日期列为 datetime 类型
                            fund_daily['日期'] = pd.to_datetime(fund_daily['日期'])
                            
                            # 将日线数据转换为周线数据
                            etf_weekly = daily_to_weekly(fund_daily)
                            
                            print(f"成功转换为{len(etf_weekly)}条周线数据")
                            return etf_weekly
                        else:
                            print(f"未获取到复权因子数据")
                            return None
                    else:
                        print(f"ts.fund_daily接口返回空数据")
                        return None
                except Exception as e:
                    print(f"ts.fund_daily接口获取{stock_code}失败: {e}")
                    return None
            else:
                print(f"未知的交易所类型: {exchange}")
                return None
        elif stock_type == 'INDEX':
            # 处理指数，使用 tushare 的 index_weekly 接口
            print(f"尝试使用ts.index_weekly接口获取{stock_code}周线数据...")
            try:
                # 调用 tushare index_weekly 接口
                index_weekly = pro.index_weekly(
                    ts_code=stock_code,
                    start_date=start_date,
                    end_date=end_date
                )
                
                if index_weekly is not None and not index_weekly.empty:
                    # 映射字段：将 tushare 字段映射到 akshare 字段
                    field_mapping = {
                        'trade_date': '日期',
                        'open': '开盘',
                        'high': '最高',
                        'low': '最低',
                        'close': '收盘',
                        'pct_chg': "涨跌幅",
                        'amount': '成交额',
                        'vol': '成交量'
                    }
                    
                    # 重命名列
                    index_weekly = index_weekly.rename(columns=field_mapping)
                    
                    # 确保只保留需要的列
                    required_cols = ['日期', '开盘', '最高', '最低', '收盘', '成交量', '成交额',"涨跌幅"]
                    available_cols = [col for col in required_cols if col in index_weekly.columns]
                    index_weekly = index_weekly[available_cols]
                    
                    # 确保日期列为 datetime 类型
                    index_weekly['日期'] = pd.to_datetime(index_weekly['日期'])
                    
                    # 确保涨跌幅字段格式统一：百分比形式，保留2位小数
                    if '涨跌幅' in index_weekly.columns:
                        index_weekly['涨跌幅'] = index_weekly['涨跌幅'] * 100
                        index_weekly['涨跌幅'] = index_weekly['涨跌幅'].round(2)
                    
                    return index_weekly
                else:
                    print(f"ts.index_weekly接口返回空数据")
                    index_weekly = None
            except Exception as e:
                print(f"ts.index_weekly接口获取{stock_code}失败: {e}")
                index_weekly = None
                return None
        else:
            print(f"未知的证券类型: {stock_type}")
            return None
    except Exception as e:
        print(f"获取{stock_code}数据时出错: {e}")
        return None


def load_securities_from_config(config_file):
    """从JSON配置文件加载证券列表"""
    if not os.path.exists(config_file):
        print(f"配置文件{config_file}不存在")
        return []

    try:
        with open(config_file, 'r', encoding='utf-8') as f:
            securities = json.load(f)
        print(f"成功从{config_file}加载{len(securities)}个证券")
        return securities
    except Exception as e:
        print(f"加载配置文件失败: {e}")
        return []


def get_latest_pct_chg(data):
    """
    获取最近一条记录的涨幅值

    参数:
    data (pandas.DataFrame): 包含股票或ETF数据的DataFrame，必须包含'收盘'列

    返回:
    float: 涨幅百分比，如2.5表示上涨2.5%，-1.3表示下跌1.3%
           如果数据不足或发生错误，返回None
    """
    if data is None or data.empty or len(data) < 2:
        print("数据不足，无法计算涨幅")
        return None

    try:
        # 获取最后两行数据
        latest = data.iloc[-1]  # 最新记录
        return latest['涨跌幅']
        previous = data.iloc[-2]  # 前一条记录

        # # 计算涨幅：(最新收盘价-前收盘价)/前收盘价*100%
        # increase = (latest['收盘'] - previous['收盘']) / previous['收盘'] * 100
        # return round(increase, 2)
    except Exception as e:
        print(f"计算涨幅时出错: {e}")
        return None

def main(config_file  : str ='config/report_em_item_config.json'):
    # 配置文件路径
    # config_file = 'config/report_em_item_config.json'

    # 从配置文件加载证券列表
    securities = load_securities_from_config(config_file)

    if not securities:
        print("没有可获取数据的证券，程序退出")
        return

    # 结果字典
    results = {}

    # 获取当前日期
    current_date = datetime.now()
    # 计算一个月前的日期
    one_month_ago = current_date - relativedelta(months=2)
    # 格式化为yyyyMMdd
    start_date = one_month_ago.strftime("%Y%m%d")
    end_date = current_date.strftime("%Y%m%d")

    # 遍历获取每个证券的周线数据
    for security in securities:
        name = security['name']
        ts_code = security['tsCode']
        item_type = security['itemType']
        print(f"\n正在获取{name}周线数据...")
        data = get_stock_weekly_data(security, start_date=start_date, end_date=end_date)

        if data is not None and not data.empty:
            results[name] = data
            print(f"{name}数据获取成功，共{len(data)}条记录")
        else:
            print(f"{name}数据获取失败或返回空数据")

    # # 打印结果概要
    # print("\n数据获取完成！以下是各证券的周线数据概要：")
    # for name, data in results.items():
    #     if data is not None and not data.empty:
    #         print(f"\n{name}周线数据概要:")
    #         print(data.tail())  # 打印后几行数据

    # # 保存数据到CSV文件（可选）
    # save_to_csv = input("\n是否将数据保存到CSV文件？(y/n): ")
    # if save_to_csv.lower() == 'y':
    #     # 创建保存目录
    #     if not os.path.exists('../ak/stock_data'):
    #         os.makedirs('../ak/stock_data')
    #
    #     for name, data in results.items():
    #         if data is not None and not data.empty:
    #             filename = f"stock_data/{name}_weekly_data.csv"
    #             data.to_csv(filename, index=False, encoding='utf-8-sig')
    #             print(f"{name}数据已保存到 {filename}")


if __name__ == "__main__":
    config_file = './config/report_em_item_config.json'
    main(config_file = config_file)
    config_file = './config/report_futu_item_config.json'
    main(config_file=config_file)
    config_file = './config/report_baseline_item_config.json'
    main(config_file=config_file)
